#!/usr/bin/python

# nextbusscrape.py: Scrape the NextBus API and create JSON or obj for the
# worldwide transit stop database.
# Formerly nextbus2json.py

# Copyright 2011 Portland Transport

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#   http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Authors:
# Matt Conway: main code

from nextbus import *
import json
from time import sleep

# First, check that the agency exists, and get the NextBus Agency() obj.

class NextBusScraper:
    def __init__(self, agency, timeout=10, quiet=True, route_type=None,
                 route_url=None, usetags=False, onlyparse=0):
        self.agency = agency
        self.timeout = timeout
        self.quiet = quiet
        self.route_type = route_type
        self.route_url = route_url
        self.usetags = usetags
        self.onlyparse = onlyparse

        self.routes = get_all_routes_for_agency(self.agency)
        if self.routes == []:
            raise NameError, 'Agency %s does not exist or has no routes defined' % self.agency

    def getTimeEstimate(self):
        '''Returns tuple of (number of routes, time estimate)'''
        return (len(self.routes), (len(self.routes)*self.timeout)/60)

    def fetchAllStops(self):
        self.stops = dict()
        # Now, loop through the routes, populating the dictionary
        i = 1
        for route in self.routes:
            if self.onlyparse != 0 and i > self.onlyparse:
                break

            # Enforce time between queries
            sleep(self.timeout)

            if not self.quiet:
                print 'Processing route {n}/{m} - {tag}'.format(n=i, m=len(self.routes), tag=route.title)

            # Get the stops on the route
            rc = get_route_config(self.agency, route.tag)

            # Loop over the stops
            for stop in rc.stops:
                if self.usetags:
                    stopid = stop.tag
                else:
                    stopid = stop.stop_id

                # Check if the stop ID is in the database
                if not self.stops.has_key(stopid):
                    # Add the stop to the database
                    # routes is populated later
                    self.stops[stopid] = dict(
                        stop_desc='', # Not provided
                        # by NextBus
                        position=None, # Couldn't find this in GTFS docs
                        location_type=0, # We don't support stations right now
                        stop_lon=stop.longitude,
                        stop_lat=stop.latitude,
                        stop_code=stop.tag,
                        direction=stop.direction_tag,
                        stop_name=stop.title,
                        routes=[], # Populated later
                        stop_id=stopid,
                        geometry={'coordinates':[stop.latitude, stop.longitude], 'type':'Point'},
                        agency='NextBus:' + self.agency,
                        zone_id=None # NextBus doesn't deal with fares
                        )


                # Closes if not stops...
                # Now, add this route to the route list
                rurl = self.route_url
                # If it isn't None, we need to do a replace
                if rurl:
                    rurl = rurl.format(route=route.tag, routelong=route.title,
                                       agency=self.agency)

                self.stops[stopid]['routes'].append(
                    dict(route_type=self.route_type,
                         route_url=rurl,
                         route_long_name=route.title,
                         route_short_name=route.tag)
                    )
            i += 1
        
        self.stopsJSON = json.dumps(self.stops) 


if __name__ == '__main__':
    from optparse import OptionParser

    # Options and arguments:
    # There is one required argument: the NextBus tag of the agency you want to
    # configure (easily obtained by typing list at the first prompt in the
    # NextBus shell).

    # Options
    # -w: Timeout between NextBus requests

    optparser = OptionParser(usage='%prog agency-tag')

    optparser.add_option('-w', '--wait', dest='wait', type='int',
                        help='The time between requests to the NextBus service. You are not supposed to query more than every 10 seconds. Default = 10',
                        metavar='SECONDS', default=10)

    optparser.add_option('-o', '--outfile', dest='out', type='string',
                         help='Outfile to write to. Defaults to <AGENCY TAG>.json', 
                         metavar='OUTFILE', default=None)

    optparser.add_option('-r', '--route-type', dest='route_type', type='int',
                         help='''Set the GTFS route_type value for single-mode
    transit systems. Possible values are 0-7, see the GTFS docs for types.''',
                         metavar='TYPE', default=None)

    optparser.add_option('-u', '--route-url', dest='route_url', type='string',
                         help='''Set the route URL. {route} will be replaced with
    the route short number, {routelong} will be replaced with the route full name,
    and {agency} will be replaced with the NextBus agency tag''',
                         metavar='URL', default=None)

    # This is because some agencies don't have stopId's - Portland Streetcar in
    # particular.
    optparser.add_option('-t', '--use-tags-as-ids', dest='usetags', 
                         action='store_true', default=False,
                         help='''Use the stop tags instead of the stop IDs
    for storing stops in the database. Use if you end up with only one stop in 
    the output.''')


    options, args = optparser.parse_args()

    if len(args) < 1:
        print optparser.print_usage()
        exit()


    # Open the file now, so we don't wait 31 minutes only to find that 
    # it's permission denied.
    if options.out:
        outf = open(options.out, 'w')
    else:
        outf = open(args[0] + '.json', 'w')

    nbp = NextBusScraper(agency=args[0], timeout=options.wait, quiet=False,
                        route_type=options.route_type,
                        route_url=options.route_url,
                        usetags=options.usetags)

    te = nbp.getTimeEstimate()
    print '''Agency %s has %s routes. Fetching them will take approximately
%s minute(s). Press Ctrl-C within five seconds to abort.''' % (args[0], te[0], te[1])
    del te
    sleep(5)

    nbp.fetchAllStops()

    outf.write(nbp.stopsJSON)
    outf.close()
    
    print '%s stops processed' % len(nbp.stops)
